/* MC 723 
   GRUPO 06 URNA ELETRONICA
   Andre Frossard
   Diego Machado
   Newton Costa
   Matheus Pereira
   */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "urna.h"
#include "whirlpool-algorithm.h"

#define FUNDO              0xe4e4e4
#define ADDR_VIDEO         0x1000000
#define ADDR_KEYBOARD      0x2000000
#define ADDR_CARD          0x3000000
#define ADDR_COUNTER       0x4000000

#define CARD_ELEITORES     ADDR_CARD + 0x000000
#define CARD_CANDIDATOS    ADDR_CARD + 0xE00000
#define CARD_PARTIDOS      ADDR_CARD + 0xF00000
#define CARD_MESARIOS      ADDR_CARD + 0xF80000
/*
 * Apontadores para as posicoes correntes de leitura
 * e escrita dos arquivos do cartao e apontadores para
 * a ultima posicao escrita (fim do arquivo).
 * Eh necessario inicializar as variaveis com valores
 * quaisquer senao ocorrem erros quando elas sao usadas
 */

int *pos_eleitores = (int *) 0x100000;
int *fim_eleitores = (int *) 0x200000;

int *pos_candidatos = (int *) 0x300000;
int *fim_candidatos = (int *) 0x400000;

int *pos_partidos = (int *) 0x3000000;
int *fim_partidos = (int *) 0x3000000;

int *pos_mesarios = (int *) 0x3000000;
int *fim_mesarios = (int *) 0x3000000;

int comunicacao =2; // 0 -> sai do programa 
// 1 -> proc1 espera proc2
// 2 -> proc2 espera proc1

int voto = 0; // indica para o proc2 qual foi o ultimo voto.

/*
 * union que servira para interpretarmos os dados lidos
 * do cartao tanto como valores inteiros (4 bytes) quanto
 * como uma string de 4 caracteres
 */
union t32b {
  int value;
  char string[4];
};

/*
 * Estruturas que representam os registros gravados em cada
 * arquivo. Elas tem o intuito de facilitar a manipulacao
 * dos arquivos, uma vez que os registros dentro do arquivo]
 * tem tamanho fixo
 */

// Estrutura que guarda um registro de Eleitor
typedef struct eleitor {
  int titulo;
  char nome[80];
  int ja_votou;
} Eleitor;

// Estrutura que guarda um registro de Candidato
typedef struct candidato {
  int numero;
  char nome[80];
  int partido;
  int num_votos;
  char hash[128];
} Candidato;

// Estrutura que guarda um registro de Partido
typedef struct partido {
  int numero;
  char nome[80];
  char sigla[8];
  int num_votos;
  char hash[128];
} Partido;

// Estrutura que guarda um registro de Mesario
typedef struct mesario {
  int id;
  char senha[40];
} Mesario;


/************************************************
 ************************************************
 ********* API DE MANIPULACAO DO CARTAO *********
 ************************************************
 ************************************************/

/*
 * Funcao: card_seek()
 *
 * Simula o comportamento da funcao fseek() em C.
 *
 * @param int arq     arquivo que se deseja mudar a
 *                    posicao corrente de leitura e
 *                    escrita. Deve receber uma das
 *                    quatro constantes CARD_*
 * @param int offset  numero de bytes que se deseja
 *                    caminhar no arquivo
 * @param int whence  a partir de qual posicao deve
 *                    ser somado offset bytes para
 *                    se chegar a nova posicao. Deve
 *                    receber um dos tres valores:
 *                      SEEK_SET: inicio do arquivo
 *                      SEEK_CUR: posicao corrente
 *                      SEEK_END: fim do arquivo
 *
 * @return int  1 em caso de sucesso ou 0 em caso de
 *              falha
 */
int card_seek(int arq, int offset, int whence) {
  int *fp;
  int addr_end;

  switch(arq) {
    case CARD_ELEITORES:
      fp = pos_eleitores;
      addr_end = *fim_eleitores;
      break;
    case CARD_CANDIDATOS:
      fp = pos_candidatos;
      addr_end = *fim_candidatos;
      break;
    case CARD_PARTIDOS:
      fp = pos_partidos;
      addr_end = *fim_partidos;
      break;
    case CARD_MESARIOS:
      fp = pos_mesarios;
      addr_end = *fim_mesarios;
      break;
    default:
      return 0;
  }

  switch(whence) {
    case SEEK_SET:
      *fp = arq + offset; 
      break;
    case SEEK_CUR:
      *fp += offset;
      break;
    case SEEK_END:
      *fp = addr_end - offset;
      break;
    default:
      return 0;
  }

  return 1;

}

/*
 * Funcao: card_read()
 *
 * Simula o comportamento da funcao fread() em C.
 * A funcao lera size*count bytes do arquivo arq
 * e colocara o dado no vetor apontado por buf.
 *
 * @param void* buf   buffer que contera o dado lido
 *                    apos a execucao da leitura
 * @param int   size  tamanho do tipo de dado a ser
 *                    lido
 * @param int   count numero de elementos do tamanho
 *                    size que deve ser lido
 * @param int   arq   arquivo que se deseja realizar
 *                    a leitura. Deve receber uma das
 *                    quatro constantes CARD_*
 *
 * @return int  o numero de itens realmente lidos. Em
 *              caso de sucesso esse numero sera igual
 *              a count. Se houver algum tipo de erro
 *              (por exemplo, fim do arquivo) o valor
 *              retornado sera menor do que count
 */
int card_read(void *buf, int size, int count, int arq) {
  union t32b data;
  int **fp;
  int i, j, ret = 0;
  char tmp;

  // As leituras devem ser sempre multiplas de 4 bytes, seguindo
  // a forma que o cartao de memoria implementa a leitura
  if(size*count % 4 != 0) {
    printf("Erro: o numero de bytes lidos deve ser multiplo de 4");
    exit(-1);
  }

  switch(arq) {
    case CARD_ELEITORES:
      fp = (int **) pos_eleitores;
      break;
    case CARD_CANDIDATOS:
      fp = (int **) pos_candidatos;
      break;
    case CARD_PARTIDOS:
      fp = (int **) pos_partidos;
      break;
    case CARD_MESARIOS:
      fp = (int **) pos_mesarios;
      break;
    default:
      return -1;
  }

  for(i = 0; i < size*count; i+=4) {
    data.value = **fp; // Le o dado do cartao
    // Quando o cartao atinge o fim de arquivo ele retorna o caracter
    // '\n' nos quatro bytes lidos. Dessa forma, comparamos os quatro
    // bytes retornados com '\n' para verficar se foi atingido o fim
    // de arquivo
    if( (data.string[0] == '\n') && (data.string[1] == '\n') && (data.string[2] == '\n') && (data.string[3] == '\n') ) {
      return 0;
    }

    // Incremento da posicao corrente de leitura e escrita do arquivo
    (*fp)++;

    // Se o dado a ser lido eh do tipo inteiro, copiamos direto no buffer
    // de retorno
    if(size == sizeof(int)) {
      memcpy(buf, &data, 4);
      ret++;
    }
    // Se o dado for uma cadeia de caracteres, devemos fazer a conversao
    // de little endian para big endian
    else {
      tmp = data.string[0];
      data.string[0] = data.string[3];
      data.string[3] = tmp;
      tmp = data.string[1];
      data.string[1] = data.string[2];
      data.string[2] = tmp;
      for(j = 0; (j < 4) && (ret < size*count); j++) {
        memcpy(buf+ret, data.string+j, 1);
        ret++;
      }
    }
  }
  return ret;
}

/*
 * Funcao: card_write()
 *
 * Simula o comportamento da funcao fwrite() em C.
 * A funcao escrevera size*count bytes do buffer
 * apontado por buf no arquivo arq.
 *
 * @param void* buf   buffer que contem o dado a ser
 *                    escrito
 * @param int   size  tamanho do tipo de dado a ser
 *                    escrito
 * @param int   count numero de elementos do tamanho
 *                    size que deve ser escrito
 * @param int   arq   arquivo que se deseja realizar
 *                    a escrita. Deve receber uma das
 *                    quatro constantes CARD_*
 *
 * @return int  o numero de bytes realmente escritos. Em
 *              caso de sucesso esse numero sera igual
 *              a size*count. Se houver algum tipo de erro
 *              o valor retornado sera menor do que
 *              size*count
 */
int card_write(void *buf, int size, int count, int arq) {
  union t32b data;
  int **fp, *end;
  int i, j, ret = 0;
  char tmp;

  // As escritas devem ser sempre multiplas de 4 bytes, seguindo
  // a forma que o cartao de memoria implementa a escrita
  if(size*count % 4 != 0) {
    printf("Erro: o numero de bytes escritos deve ser multiplo de 4");
    exit(-1);
  }

  switch(arq) {
    case CARD_ELEITORES:
      fp = (int **) pos_eleitores;
      end = fim_eleitores;
      break;
    case CARD_CANDIDATOS:
      fp = (int **) pos_candidatos;
      end = fim_candidatos;
      break;
    case CARD_PARTIDOS:
      fp = (int **) pos_partidos;
      end = fim_partidos;
      break;
    case CARD_MESARIOS:
      fp = (int **) pos_mesarios;
      end = fim_mesarios;
      break;
    default:
      return -1;
  }

  for(i = 0; i < size*count; i+=4) {
    memset(data.string, '\0', 4);
    for(j = 0; (j < 4) && (ret < size*count); j++) {
      memcpy(data.string+j, buf+ret, 1);
      ret++;
    }

    if(size == sizeof(char)) {
      tmp = data.string[0];
      data.string[0] = data.string[3];
      data.string[3] = tmp;
      tmp = data.string[1];
      data.string[1] = data.string[2];
      data.string[2] = tmp;
    }

    **fp = data.value;
    (*fp)++;
  }

  // Atualiza o apontador do fim do arquivo
  // se a ecrita ultrapassar o antigo endereco
  // de fim
  if( ((int) *fp) > *end ) {
    *end = (int) *fp;
  }
  return ret;
}

/*
 * Funcao: card_rewind()
 *
 * Simula o comportamento da funcao rewind() em C.
 * Retorna o apontador da posicao corrente de leitura
 * e escrita para o inicio do arquivo.
 *
 * @param int   arq   arquivo que se deseja retornar o
 *                    apontador de leitura e escrita.
 *                    Deve receber uma das quatro
 *                    constantes CARD_*
 */
void card_rewind(int arq) {
  card_seek(arq, 0, SEEK_SET);
}

/*
 * Funcao: prox_eleitor()
 *
 * Le o proximo registro de eleitore que estiver apontado
 * pelo apontador de leitura e escrita do arquivo
 * CARD_ELEITORES e incrementa o apontado no numero de bytes
 * do tamanho do registro
 *
 * @return Eleitor  registro do eleitor lido. Se o fim de
 *                  arquivo foi atingido, retorna -1 no
 *                  campo Eleitor::titulo
 */
Eleitor prox_eleitor() {
  Eleitor e;

  if(card_read(&(e.titulo), sizeof(int), 1, CARD_ELEITORES) > 0) {
    card_read(e.nome, sizeof(char), 80, CARD_ELEITORES);
    card_read(&(e.ja_votou), sizeof(int), 1, CARD_ELEITORES);
  }
  else {
    e.titulo = -1;
  }

  return e;
}

/*
 * Funcao: prox_candidato()
 *
 * Le o proximo registro de candidato que estiver apontado
 * pelo apontador de leitura e escrita do arquivo
 * CARD_CANDIDATOS e incrementa o apontado no numero de bytes
 * do tamanho do registro
 *
 * @return Candidato  registro do candidato lido. Se o fim de
 *                    arquivo foi atingido, retorna -1 no
 *                    campo Candidato::numero
 */
Candidato prox_candidato() {
  Candidato c;

  if(card_read(&(c.numero), sizeof(int), 1, CARD_CANDIDATOS) > 0) {
    card_read(c.nome, sizeof(char), 80, CARD_CANDIDATOS);
    card_read(&(c.partido), sizeof(int), 1, CARD_CANDIDATOS);
    card_read(&(c.num_votos), sizeof(int), 1, CARD_CANDIDATOS);
    card_read(c.hash, sizeof(char), 128, CARD_CANDIDATOS);
  }
  else {
    c.numero = -1;
  }

  return c;
}

/*
 * Funcao: prox_partido()
 *
 * Le o proximo registro de partido que estiver apontado
 * pelo apontador de leitura e escrita do arquivo
 * CARD_PARTIDOS e incrementa o apontado no numero de bytes
 * do tamanho do registro
 *
 * @return Partido  registro do partido lido. Se o fim de
 *                  arquivo foi atingido, retorna -1 no
 *                  campo Partido::numero
 */
Partido prox_partido() {
  Partido p;

  if(card_read(&(p.numero), sizeof(int), 1, CARD_PARTIDOS) > 0) {
    card_read(p.nome, sizeof(char), 80, CARD_PARTIDOS);
    card_read(p.sigla, sizeof(char), 8, CARD_PARTIDOS);
    card_read(&(p.num_votos), sizeof(int), 1, CARD_PARTIDOS);
    card_read(p.hash, sizeof(char), 128, CARD_PARTIDOS);
  }
  else {
    p.numero = -1;
  }

  return p;
}

/*
 * Funcao: prox_mesario()
 *
 * Le o proximo registro de mesario que estiver apontado
 * pelo apontador de leitura e escrita do arquivo
 * CARD_MESARIOS e incrementa o apontado no numero de bytes
 * do tamanho do registro
 *
 * @return Mesario  registro do mesario lido. Se o fim de
 *                  arquivo foi atingido, retorna -1 no
 *                  campo Mesario::id
 */
Mesario prox_mesario() {
  Mesario m;

  if(card_read(&(m.id), sizeof(int), 1, CARD_MESARIOS) > 0) {
    card_read(m.senha, sizeof(char), 40, CARD_MESARIOS);
  }
  else {
    m.id = -1;
  }

  return m;
}

/*
 * Funcao: busca_eleitor()
 *
 * Busca o registro do eleitor cujo Titulo de Eleitor for
 * igual a titulo
 *
 * @param int titulo  Titulo do eleitor que se deseja buscar
 *
 * @return Eleitor  registro do eleitor encontrado. Retorna
 *                  -1 no campo Eleitor::titulo se a busca
 *                  nao encontrou nenhum registro
 */
Eleitor busca_eleitor(int titulo) {
  Eleitor e;

  card_rewind(CARD_ELEITORES);

  do {
    e = prox_eleitor();
  } while ( (e.titulo != titulo) && (e.titulo != -1) );

  return e;
}

/*
 * Funcao: busca_candidato()
 *
 * Busca o registro do candidato cujo Numero for igual a
 * numero
 *
 * @param int numero  numero do candidato que se deseja buscar
 *
 * @return Candidato  registro do candidato encontrado. Retorna
 *                    -1 no campo Candidato::numero se a busca
 *                    nao encontrou nenhum registro
 */
Candidato busca_candidato(int numero) {
  Candidato c;

  card_rewind(CARD_CANDIDATOS);

  do {
    c = prox_candidato();
  } while ( (c.numero != numero) && (c.numero != -1) );

  return c;
}

/*
 * Funcao: busca_partido()
 *
 * Busca o registro do partido cujo Numero for igual a
 * numero
 *
 * @param int numero  numero do partido que se deseja buscar
 *
 * @return Partido  registro do partido encontrado. Retorna
 *                  -1 no campo Partido::numero se a busca
 *                  nao encontrou nenhum registro
 */
Partido busca_partido(int numero) {
  Partido p;

  card_rewind(CARD_PARTIDOS);

  do {
    p = prox_partido();
  } while ( (p.numero != numero) && (p.numero != -1) );

  return p;
}

/*
 * Funcao: busca_mesario()
 *
 * Busca o registro do mesario cujo Identificador for igual
 * a id
 *
 * @param int id  id do mesario que se deseja buscar
 *
 * @return Mesario  registro do mesario encontrado. Retorna
 *                  -1 no campo Mesario::id se a busca
 *                  nao encontrou nenhum registro
 */
Mesario busca_mesario(int id) {
  Mesario m;

  card_rewind(CARD_MESARIOS);

  do {
    m = prox_mesario();
  } while ( (m.id != id) && (m.id != -1) );

  return m;
}

/*
 * Funcao: eleitor_votou()
 *
 * Muda o campo Eleitor::ja_votou quando o mesmo
 * tiver votado
 * 
 * @param int titulo  Titulo do eleitor
 *
 * @return 1 em caso de sucesso ou 0 em caso de falha
 */
int eleitor_votou(int titulo) {
  Eleitor e;
  int votou = 1;

  e = busca_eleitor(titulo);
  if(e.titulo != -1) {
    card_seek(CARD_ELEITORES, -sizeof(int), SEEK_CUR);
    card_write(&votou, sizeof(int), 1, CARD_ELEITORES);
    return 1;
  }

  return 0;
}

/*
 * Funcao: votar_candidato()
 *
 * Incrementa o numero de votos do candidato quando
 * ele for votado
 *
 * @param int numero  Numero do candidato
 *
 * @return 1 em caso de sucesso ou 0 em caso de falha
 */
int votar_candidato(int numero) {
  Candidato c;
  int num_votos;

  c = busca_candidato(numero);
  if(c.numero != -1) {
    card_seek(CARD_CANDIDATOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
    card_seek(CARD_CANDIDATOS, -sizeof(int), SEEK_CUR); // Volta o campo num_votos
    num_votos = c.num_votos + 1;
    card_write(&num_votos, sizeof(int), 1, CARD_CANDIDATOS);
    return 1;
  }

  return 0;
}

/*
 * Funcao: votar_partido()
 *
 * Incrementa o numero de votos do partido quando
 * ele receber um voto de legenda
 *
 * @param int numero  Numero do partido
 *
 * @return 1 em caso de sucesso ou 0 em caso de falha
 */
int votar_partido(int numero) {
  Partido p;
  int num_votos;

  p = busca_partido(numero);
  if(p.numero != -1) {
    card_seek(CARD_PARTIDOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
    card_seek(CARD_PARTIDOS, -sizeof(int), SEEK_CUR); // Volta o campo num_votos
    num_votos = p.num_votos + 1;
    card_write(&num_votos, sizeof(int), 1, CARD_PARTIDOS);
    return 1;
  }

  return 0;
}

int verifica_candidatos(){
  // retorna zero se cada o de cada candidato confere com os dados do candidato
  // caso contrario retorna -1
  Candidato c;
  int i;
  char string[100]; 
  char numero[5];
  unsigned char hash[WP_DIGEST_SIZE * 2];
  card_rewind(CARD_CANDIDATOS);
  c = prox_candidato();
  while (c.numero != -1) {
    for (i = 0; i < 100; i++) string[i] = '\0';
    for (i = 0; i < 5; i++) numero[i] = '\0';
    sprintf(numero, "%d", c.numero);
    strcat(string, numero);
    strcat(string, c.nome);
    for(i=0; i < 5;i++) numero[i] = '\0';
    sprintf(numero, "%d", c.partido);
    for(i=0; i < 5;i++) numero[i] = '\0';
    sprintf(numero, "%d", c.num_votos);
    strcat(string, numero);
    whirlpool_calc_hex(string, hash);
    printf("VERIFICANDO STRING: %s\nHASH1: %s\nHASH2: %s\n", string, hash, c.hash);
    if (strcmp(hash, c.hash) != 0)
      return -1;
    c = prox_candidato();
  } 
  return 0;
}

int verifica_partidos(){
  // retorna zero se o hash de cada partido confere com os dados do candidato
  // caso contrario retorna -1
  Partido p;
  int i;
  char string[100]; 
  char numero[5];
  unsigned char hash[WP_DIGEST_SIZE * 2];
  card_rewind(CARD_PARTIDOS);
  p = prox_partido();
  while (p.numero != -1) {
    for (i = 0; i < 100; i++) string[i] = '\0';
    for (i = 0; i < 5; i++) numero[i] = '\0';

    sprintf(numero, "%d", p.numero);
    strcat(string, numero);
    strcat(string, p.nome);
    strcat(string, p.sigla);
    for(i=0; i < 5;i++) numero[i] = '\0';
    sprintf(numero, "%d", p.num_votos);
    strcat(string, numero);
    whirlpool_calc_hex(string, hash);
    printf("VERIFICANDO STRING: %s\nHASH1: %s\nHASH2: %s\n", string, hash, p.hash);
    if (strcmp(hash, p.hash) != 0)
      return -1;
    p = prox_partido();
  } 
  return 0;
}

int verifica_urna(){
  //retorna zero caso os dados dos partidos e candidatos estao integros
  //ou seja: o hash gravado confere com os dados.
  if ((verifica_partidos() == 0) && (verifica_candidatos() == 0))
    return 0;
}

/***********************************************
 ***** FIM DA API DE MANIPULACAO DO CARTAO *****
 ***********************************************/

int proc1() {
  int autenticacao,votacao; // variaveis booleanas
  char nomepartido[30];
  char mesario[9];
  char senha[3];
  char deputado[4];
  char teleitor[5]; // titulo eleitor
  int num,conta;
  char tecla;
  int *video, *espera;
  int i,x, y, posicao, posicao2, posy;
  char buff[100];
  espera = (int *) ADDR_COUNTER;
  // Y orienta o eixo VERTICAL   - Vai de 0 a 480
  // X orienta o eixo HORIZONTAL - Vai de 0 a 640

  Eleitor e;
  Candidato c;
  Mesario m;
  Partido p;

  *pos_eleitores = CARD_ELEITORES;
  *fim_eleitores = CARD_ELEITORES;
  *pos_candidatos = CARD_CANDIDATOS;
  *fim_candidatos = CARD_CANDIDATOS;
  *pos_partidos = CARD_PARTIDOS;
  *fim_partidos = CARD_PARTIDOS;
  *pos_mesarios = CARD_MESARIOS;
  *fim_mesarios = CARD_MESARIOS;

  // 1-   Funcionario se autentica e autoriza eleitor
  // 1.1 -busca no cartao ve se eleitor existe, se senha esta correta
  // 2-    SE ok,  Eleitor assume a urna e digita numero
  // 2.1 - verifica-se o numero do candidato. se existir exibe nome, se nao exibe NULO
  do{
    conta=0;     // INICIALIZACAO DAS VARIAVEIS E TELA
    posicao=0;
    posicao2=0;
    autenticacao=1;
    votacao=1;
    voto = 0;
    for(i=0; i<4;i++)
      deputado[i]='\0';
    impressaoinicial(1);
    do{
      do {(tecla = letecla());}
      while (!((tecla =='c')||(tecla =='q')||(tecla ==13) || (tecla >= '0' && tecla <= '9'))); 
      // So aceita as teclas ENTER, 'q',para sair, 'c' para corrigir ou as numericas entre 0 e 9

      if (tecla =='q'){
        comunicacao=0;
        exit(0);
        //autenticacao =0; // so para teste, pra n ter q ficar passando ela autenticacao toda hora de testar
      }
      if ((tecla >='0') && (tecla <='9') ){ 
        if (conta >= 9){ // lemos todos os 9 numeros do id
          if(conta <=12) { //enquanto a senha nao foi lida por completo
            posicao=0;
            senha[conta-9] = tecla;
            conta++;
            x= 150; //POSICAO HORIZONTAL DE ONDE SERA IMPRESSO	    
            y= 250; //POSICAO VERTICAL ONDE SERA IMPRESSO
            imprimenumero(tecla,x+posicao2,y);
            posicao2 += ESPACAMENTO;}
          else if (conta <=18) { //ja lemos todos os numeros para o usuario e senha
            teleitor[conta-13]=tecla;
            conta++;
            x= 310; //POSICAO HORIZONTAL DE ONDE SERA IMPRESSO	    
            y= 300; //POSICAO VERTICAL ONDE SERA IMPRESSO
            imprimenumero(tecla,x+posicao,y);
            posicao += ESPACAMENTO;
            if (conta ==19) { //ja lemos todos os numeros para o usuario e senha e eleitor
              imprime("Pressione ENTER para confirmar", 50, 360); 
              imprime("ou ''c'' para corrigir", 150, 390); 
              //printf("eleitor %s\n",teleitor);
            }
          }    
        }
        else { // ainda ha numeros a ler
          mesario[conta] = tecla;
          conta++;
          x= 300; //POSICAO HORIZONTAL DE ONDE SERA IMPRESSO	    
          y= 200; //POSICAO VERTICAL ONDE SERA IMPRESSO
          imprimenumero(tecla,x+posicao,y);
          posicao += ESPACAMENTO;
        }
      }// if numero entre 1 e 9
      else
        if ((tecla ==13) && (conta == 19)){ // 13 eh o ENTER ! 
          //devemos entao comparar as informacoes recebidas para autenticar o mesario
          //procura no cartao o id e senha
          preenchetela(FUNDO); 
          imprime("Urna eletronica", 170, 025);
          linha(0,100,640,100,'h', PRETO);
          imprime("Verificando dados...", 50, 110);
          imprime("Por favor aguarde...", 130, 150);
          m = busca_mesario(atoi(mesario));
          e = busca_eleitor(atoi(teleitor));
          printf("\nEleitor:\ne.titulo = %d\ne.nome = %s\ne.ja_votou = %d\n", e.titulo, e.nome, e.ja_votou);
          if(m.id == -1) {
            imprime("Mesario nao encontrado", 90, 250);  
            imprime("Pressione ENTER para acessar", 50, 350);  
            imprime("a tela de autenticacao", 110, 385);   
            do {(tecla = letecla());}
            while (!(tecla ==13));
            conta=0;
            posicao=0;
            posicao2=0;
            impressaoinicial(1);
          }
          else {
            if (atoi(m.senha)==atoi(senha)){
              // Senha do mesario eh valida. Prosseguimos na validacao do eleitor
              if (e.titulo != -1){ // eleitor existe
                if(e.ja_votou == 0) { // Pessoa ainda nao votou
                  imprime("Mesario logado com sucesso", 75, 250); 
                  retangulo(70,250,550,290,PRETO);
                  imprime("Mesario, o eleitor ja pode votar.", 30, 315);  
                  imprime("Pressione ENTER para acessar", 50, 350);  
                  imprime("a tela de votacao", 110, 385);
                  autenticacao = 0; // Completamos a autenticacao
                  do {(tecla = letecla());}
                  while (!( (tecla ==13) || (tecla == 'l') || (tecla == 'v'))); 
                }
                else {// Eleitor ja votou
                  imprime("Mesario logado com sucesso", 75, 200); 
                  imprime("Eleitor ja votou!", 150, 250); 
                  retangulo(20,250,620,294,PRETO);
                  imprime("Pressione ENTER para voltar", 50, 350);  
                  imprime("a tela de autenticacao", 110, 385);
                  do {(tecla = letecla());}
                  while (!( (tecla ==13) || (tecla == 'l') || (tecla == 'v'))); 
                  conta=0;
                  posicao=0;
                  posicao2=0;
                  impressaoinicial(1);
                }
              }
              else { // Eleitor nao existe
                imprime("Mesario logado com sucesso", 75, 200); 
                imprime("Eleitor nao cadastrado nessa urna", 25, 250); 
                retangulo(20,250,620,294,PRETO);
                imprime("Pressione ENTER para voltar", 50, 350);  
                imprime("a tela de autenticacao", 110, 385);
                do {(tecla = letecla());}
                while (!( (tecla ==13) || (tecla == 'l') || (tecla == 'v'))); 
                conta=0;
                posicao=0;
                posicao2=0;
                impressaoinicial(1);
              }
              if(tecla == 'l') {
                preenchetela(FUNDO); 
                imprime("Resultado da Votacao", 150, 025);
                linha(0,100,640,100,'h', PRETO);
                imprime("Votos dos partidos:", 20, 120);
                card_rewind(CARD_PARTIDOS);
                p = prox_partido();
                posy = 160;
                while(p.numero != -1) {
                  if (posy > 400) {
                    imprime("'p': ir para a pagina seguinte", 40, 440);
                    do {(tecla = letecla());}
                    while (!(tecla == 'p'));
                    preenchetela(FUNDO);
                    imprime("Resultado da Votacao", 150, 025);
                    linha(0,100,640,100,'h', PRETO);
                    imprime("Votos dos partidos:", 20, 120);
                    posy = 160;
                  }
                  sprintf(buff, "%02d %s %04d", p.numero, p.sigla, p.num_votos);
                  imprime(buff, 50, posy);
                  p = prox_partido();
                  posy += 35;
                }
                imprime("'p': ver votacao dos candidatos", 40, 440);
                do {(tecla = letecla());}
                while (!(tecla == 'p'));
                preenchetela(FUNDO); 
                imprime("Resultado da Votacao", 150, 025);
                linha(0,100,640,100,'h', PRETO);
                imprime("Votos dos candidatos:", 20, 120);
                card_rewind(CARD_CANDIDATOS);
                c = prox_candidato();
                posy = 160;
                while(c.numero != -1) {
                  if (posy > 400) {
                    imprime("'p': ir para a pagina seguinte", 40, 440);
                    do {(tecla = letecla());}
                    while (!(tecla == 'p'));
                    preenchetela(FUNDO);
                    imprime("Resultado da Votacao", 150, 025);
                    linha(0,100,640,100,'h', PRETO);
                    imprime("Votos dos candidatos:", 20, 120);
                    posy = 160;
                  }
                  if(c.numero == 99998) {
                    sprintf(buff, "Brancos %04d", c.num_votos);
                  }
                  else if(c.numero == 99999) {
                    sprintf(buff, "Nulos %04d", c.num_votos);
                  }
                  else {
                    sprintf(buff, "%d %04d", c.numero, c.num_votos);
                  }
                  imprime(buff, 50, posy);
                  c = prox_candidato();
                  posy += 35;
                }
                imprime("Pressione ENTER para voltar", 50, 440);  
                do {(tecla = letecla());}
                while (!(tecla ==13)); 
                conta=0;
                posicao=0;
                posicao2=0;
                autenticacao=1;
                impressaoinicial(1);
              }
              if(tecla == 'v') {
                preenchetela(FUNDO); 
                imprime("Verificando dados...", 150, 025);
                linha(0,100,640,100,'h', PRETO);
                if (verifica_urna() == 0){               
                  retangulo(70,250,395,290,PRETO);
                  imprime("Dados Inalterados", 75, 250);
                  imprime("Pressione ENTER para continuar", 50, 350);  
                  imprime("para a tela de votacao", 110, 385);
                  do {(tecla = letecla());}
                  while (!(tecla ==13)); 
                  conta=0;
                  posicao=0;
                  posicao2=0;
                  autenticacao=1;
                  impressaoinicial(1);
                } else {
                  retangulo(70,250,490,290,PRETO);
                  imprime("ATENCAO: Urna Violada!", 75, 250);
                  imprime("Pressione ENTER para sair", 50, 350);  
                  imprime("Notifique as autoridades!!!", 110, 385);
                  do {(tecla = letecla());}
                  while (!(tecla ==13)); 
                  comunicacao=0;
                  exit(0);
                }
                //TELA DE VERIFICACAO DE DADOS
              }
            }
            else { // senha do mesario invalida
              imprime("Senha do mesario incorreta", 75, 250); 
              retangulo(70,250,550,290,PRETO);
              imprime("Pressione ENTER para voltar", 50, 350);  
              imprime("a tela de autenticacao", 110, 385);
              do {(tecla = letecla());}
              while (!(tecla ==13)); 
              conta=0;
              posicao=0;
              posicao2=0;
              impressaoinicial(1);
            }
          }
        }
        else
          if (tecla =='c'){ // C eh o CORRIGIR
            //Limpa e reinicia a leitura
            conta=0;
            posicao=0;
            posicao2=0;
            impressaoinicial(1);
          }
    }while(autenticacao); // do principal

    // **************** ACABOU A AUTENTICACAO ********************
    // *****************INICIO VOTACAO ***************************

    posicao =0;
    conta=0;
    preenchetela(FUNDO);
    impressaoinicial(2);

    do{
      do {(tecla = letecla());}
      while (!((tecla =='c')||(tecla =='b')||(tecla =='q')||(tecla ==13) || (tecla >= '0' && tecla <= '9'))); 
      // So aceita as teclas ENTER, 'q',para sair, 'c' para corrigir ou as numericas entre 0 e 9
      if ((tecla >='0') && (tecla <='9') ){ 
        if (conta<=4){
          //VERIFICA CANDIDATO, ETC
          x= 150; //POSICAO HORIZONTAL DE ONDE SERA IMPRESSO	    
          y= 250; //POSICAO VERTICAL ONDE SERA IMPRESSO
          deputado[conta]=tecla;
          imprimenumero(tecla,x+posicao,y);
          posicao += ESPACAMENTO;
          conta++;
          if(conta==3){ // apagar coisas do voto em legenda
            apagar("XXXXXXXXXXXXXXXX",400, 330);
            apagar("Voto na legenda", 50, 370);
            apagar("Pressione ENTER para confirmar", 50, 400); 
            apagar("ou ''c'' para corrigir", 150, 430); 
            apagarimagem();
          }
          if (conta==2){ // Voto na legenda
            // TEMPORARIO Na verdade pega o nome do partido do cartao
            p = busca_partido(atoi(deputado));
            if(p.numero != -1){//partido existe
              imprime(p.nome,400, 330);
              imprime("Voto na legenda", 50, 366);
              imprime("Pressione ENTER para confirmar", 50, 400); 
              imprime("ou ''c'' para corrigir", 150, 430); 	      
            }
          } 
          if (conta==5){ // todos os numeros foram digitados
            c = busca_candidato(atoi(deputado));
            if( (c.numero == -1) || (c.numero == 99998) || (c.numero == 99999) ) { //Voto NULO!
              imprime("VOTO NULO", 220, 310); 
              imprimeimagem(0);
            }
            else {
              imprime(c.nome,370, 315); 
              imprime(p.nome,405, 345);
              imprimeimagem(c.numero);
            }
            imprime("Pressione ENTER para confirmar", 50, 400); 
            imprime("ou ''c'' para corrigir", 150, 430);      
          }
        }
      }
      else  
        if ((conta==2) && (tecla==13)){// confirmar voto legenda
          eleitor_votou(e.titulo); // marca que o eleitor ja votou
          votar_partido(p.numero);
          voto = p.numero;
          comunicacao =1;
          votacao = 0;
          impressaoinicial(3);
          while (comunicacao==1){
            // SLEEP NO PROC1
            sleep(2000000);
          }
        }
        else
          if ((conta==5) && (tecla==13)){// confirmar voto normal
            // grava no cartao, etc
            eleitor_votou(e.titulo); // marca que o eleitor ja votou
            if ( (c.numero != -1) && (c.numero != 99998) && (c.numero != 99999) ){ //voto em candidato valido
              votar_candidato(c.numero); // marca mais um voto pro candidato
              voto = c.numero;
            }
            else { //voto nulo
              votar_candidato(99999);
              voto=99999;
            }
            comunicacao =1; // Proc1 espera proc2
            votacao = 0;
            impressaoinicial(3);
            while (comunicacao==1){
              // SLEEP NO PROC1
              sleep(2000000);
            }
          }
          else  if (tecla =='q'){
            comunicacao=0;
            exit(0);
          }
          else
            if (tecla =='c'){ // C eh o CORRIGIR
              //Limpa e reinicia a leitura
              posicao =0;
              conta=0;
              impressaoinicial(2);
              int i;
              for(i=0; i<5;i++)
                deputado[i]='\0';
            } else
              if ((tecla =='b')&& (conta==0)){ // voto em branco
                // no simulador da urna ele so eh possivel se nao houver
                // apertado nenhum numero, entao fizemos o mesmo.
                preenchetela(FUNDO);
                imprime("Urna eletronica", 170, 025);
                linha(0,100,640,100,'h', PRETO);
                imprime("VOTO EM BRANCO", 180, 250);
                imprime("Pressione ENTER para confirmar", 50, 400); 
                imprime("ou ''c'' para corrigir", 150, 430);

                do {(tecla = letecla());}
                while (!((tecla =='c')||(tecla ==13)));

                if (tecla ==13){
                  votar_candidato(99998);
                  eleitor_votou(e.titulo); // marca que o eleitor ja votou
                  voto=99998;
                  comunicacao =1;
                  votacao = 0;
                  impressaoinicial(3);
                  while (comunicacao==1)
                    // SLEEP NO PROC1
                    sleep(2000000);
                } else
                  if (tecla =='c'){
                    impressaoinicial(2);
                    posicao =0;
                    conta=0;
                  }
                // So aceita as teclas ENTER, 'q',para sair, 'c' para corrigir ou as numericas entre 0 e 9
              }
    }while(votacao);

    preenchetela(FUNDO);

    imprime("FIM !!", 300, 200);
    imprime("Obrigado por exercer ", 160, 360);
    imprime("seu papel de cidadao.", 160, 400);

    do {(tecla = letecla());}
    while (!((tecla =='q')||(tecla==13))); // Enter permite outro eleitor votar
    if (tecla =='q'){
      comunicacao=0;
      exit(0);}

  }while(1);
  exit(0);
  return 0;
}

void impressaoinicial(int fase){

  if(fase==1){ //impressao da tela da fase de autenticacao
    preenchetela(FUNDO); 
    imprime("Urna eletronica", 170, 025);
    linha(0,100,640,100,'h', PRETO);
    imprime("Id do mesario", 50, 200);
    imprime("Senha", 50, 250);
    imprime("Titulo Eleitor", 50, 300);
    imprimeretangulos(300,200,PRETO,9);
    imprimeretangulos(150,250,PRETO,4);
    imprimeretangulos(310,300,PRETO,6);
  }
  else if(fase==2) // fase de votacao
  {
    preenchetela(FUNDO);
    retangulo(429, 149, 550, 310,PRETO);
    imprime("Urna eletronica", 170, 025);
    linha(0,100,640,100,'h', PRETO);
    imprime("DIRETOR DO IC", 40, 125);
    imprimeretangulos(150,250,PRETO,5);
  }
  else if(fase==3) // fase de criptografia
  {
    preenchetela(FUNDO);
    imprime("Aguarde...", 75, 230);
    imprime("Aplicando criptografia...", 125, 260);  
    retangulo(70,230,575,300,PRETO);

  }
}
void imprimenumero(char numero,int x1, int y1){
  int *video;
  video = (int *) ADDR_VIDEO + 4*(640*y1+x1)+1228800;
  *video = numero;  
}

void imprimeimagem(int numeroimagem){
  int *video;
  video = (int *) ADDR_VIDEO + 640*480+1;
  *video = numeroimagem;
}

void imprime(char texto[],int x1, int y1){
  int *video; 
  int i, posicao2=0;
  for(i = 0; texto[i] != '\0'; i++ ) {
    //x1 POSICAO HORIZONTAL DE ONDE SERA IMPRESSO
    //y1 POSICAO VERTICAL ONDE SERA IMPRESSO
    video = (int *) ADDR_VIDEO + 4*(640*y1+x1+posicao2)+1228800;
    *video = texto[i];
    posicao2 += 18;
  }
}
void apagar(char texto[],int x1, int y1){
  int i,tmp,espaco=0;
  tmp=y1;
  for(i = 0; texto[i] != '\0'; i++ ) {
    espaco+=18;
  }
  do{
    linha(x1,y1,x1+espaco,y1,'h',FUNDO);
    y1++;
  }while(y1< tmp+38 );
}

void apagarimagem(){
  int y1=0;
  do{
    linha(430,150,549,150+y1,'h',FUNDO);
    y1++;
  }while(y1< 160 );

}

void preenchetela(int cor){
  int *video; 
  video = (int *) ADDR_VIDEO + 640*480;
  *video = cor; // cor que ira preencher a tela
}

char letecla() {
  return *((int *)( ADDR_KEYBOARD));
}

void linha(int x1, int y1, int x2, int y2, char orientacao,int cor){

  int x,y;
  int *video; 
  // x1 e y1 guardarao a posicao do primeiro pixel a ser preenchido com a cor escolhida, ou a posicao horizontal ou vertical da linha.
  // x2 e y2 guardarao a posicao do ultimo pixel a ser preenchido com a cor escolhida, ou a posicao horizontal ou vertical da linha.

  if (orientacao == 'h'){ // linha horizontal
    for(x=x1; x<=x2; x++){
      video = (int *) (ADDR_VIDEO + 640*y2 + x); // Mantem o y recebido e varia o x para preencher a linha horizontal toda
      *video = cor ;
    }
  }
  else if (orientacao == 'v'){ // linha vertical
    for(y=y1; y<=y2; y++){
      video = (int *) (ADDR_VIDEO + 640*y + x2);// Mantem o x recebido e varia o y para preencher a linha vertical toda
      *video = cor ;
    }
  }
}

void retangulo(int x1, int y1, int x2, int y2,int cor){

  linha(x1,y1,x2,y1,'h',cor);
  linha(x1,y1,x1,y2,'v',cor);
  linha(x1,y2,x2,y2,'h',cor);
  linha(x2,y1,x2,y2,'v',cor);
}

void retangulonumero(int x1, int y1, int cor){
  //recebe os mesmos parametros da funcao imprime, imprimindo um retangulo ao redor do numero no tamanho correto.
  retangulo(x1,(y1+4),(x1+ESPACAMENTO-4),(y1+32),cor);	    
}

void imprimeretangulos(int x1, int y1, int cor, int quantidade){
  //imprime 'quantidade' retangulos para que os numeros recaiam dentro deles
  int z;
  for(z=0; z<quantidade; z++){
    retangulonumero((x1+ESPACAMENTO*z), y1, cor);
  }
}

static unsigned char hex(unsigned char val)
{
  if (val < 10)
  {
    return ('0' + val);
  }
  else
  {
    return ('a' + val - 10);
  }
}

void whirlpool_calc_hex(char texto[],char hex_hash[]) {
  WP_Struct *wp;
  unsigned char hash[WP_DIGEST_SIZE];
  unsigned int i,k=0;
  wp = WP_Create();
  while(texto[k]!='\0'){
    k++;
  }
  //printf("\n\n Numero caracteres: %d \n",k);
  WP_Add(texto, k*8, wp);
  WP_Finalize(wp, hash);
  WP_Free(wp);
  char tempor;
  for (i = 0; i < sizeof(hash); i++) {
    hex_hash[i * 2] = hex(hash[i] >> 4);
    tempor = (hash[i] >> 4);
    hex_hash[i * 2 + 1] = hex(hash[i] & 0xF);
    tempor=(hash[i] & 0xF);
  }
}

void sleep(int t){
  int  *espera;
  espera = (int *) ADDR_COUNTER;
  *espera = t;
}

int proc2() {
  Partido p;
  Candidato c;
  char string[100]; 
  char numero[5];
  unsigned char hash[WP_DIGEST_SIZE * 2];
  int i;

  //cria hash para os candidatos que nao tem hash
  card_rewind(CARD_CANDIDATOS);
  c = prox_candidato();  

  while (c.numero != -1)  {
    if (c.hash[0] == '\0'){
      for (i = 0; i < 100; i++) string[i] = '\0';
      for (i = 0; i < 5; i++) numero[i] = '\0';      
      sprintf(numero, "%d", c.numero);
      strcat(string, numero);
      strcat(string, c.nome);
      for(i=0; i < 5;i++) numero[i] = '\0';
      sprintf(numero, "%d", c.partido);
      for(i=0; i < 5;i++) numero[i] = '\0';
      sprintf(numero, "%d", c.num_votos);
      strcat(string, numero);
      whirlpool_calc_hex(string, hash);
      card_seek(CARD_CANDIDATOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
      card_write(hash, sizeof(char), WP_DIGEST_SIZE*2, CARD_CANDIDATOS);
    }
    c = prox_candidato();
  } 

  //cria hash para os partidos que nao tem hash
  card_rewind(CARD_PARTIDOS);
  p = prox_partido();
  while (p.numero != -1) {
    if (p.hash[0] == '\0'){
      for (i = 0; i < 100; i++) string[i] = '\0';
      for (i = 0; i < 5; i++) numero[i] = '\0';      
      sprintf(numero, "%d", p.numero);
      strcat(string, numero);
      strcat(string, p.nome);
      strcat(string, p.sigla);
      for(i=0; i < 5;i++) numero[i] = '\0';
      sprintf(numero, "%d", p.num_votos);
      strcat(string, numero);
      whirlpool_calc_hex(string, hash);
      card_seek(CARD_PARTIDOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
      card_write(hash, sizeof(char), WP_DIGEST_SIZE*2, CARD_PARTIDOS);
    }
    p = prox_partido();
  } 

  do{
    if (comunicacao==1){
      // proc1 espera proc2
      // O proc2 tem entao que trabalhar
      for (i = 0; i < 100; i++) string[i] = '\0';
      if (voto < 100) {     //votou em partido
        p = busca_partido(voto);
        sprintf(numero, "%d", p.numero);
        strcat(string, numero);
        strcat(string, p.nome);
        strcat(string, p.sigla);
        for(i=0; i < 5;i++) numero[i] = '\0';
        sprintf(numero, "%d", p.num_votos);
        strcat(string, numero);
        whirlpool_calc_hex(string, hash);
        card_seek(CARD_PARTIDOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
        card_write(hash, sizeof(char), WP_DIGEST_SIZE*2, CARD_PARTIDOS);
      } else {
        c = busca_candidato(voto);
        sprintf(numero, "%d", c.numero);
        strcat(string, numero);
        strcat(string, c.nome);
        for(i=0; i < 5;i++) numero[i] = '\0';
        sprintf(numero, "%d", c.partido);
        for(i=0; i < 5;i++) numero[i] = '\0';
        sprintf(numero, "%d", c.num_votos);
        strcat(string, numero);
        whirlpool_calc_hex(string, hash);
        card_seek(CARD_CANDIDATOS, -128*sizeof(char), SEEK_CUR); // Volta o campo de hash
        card_write(hash, sizeof(char), WP_DIGEST_SIZE*2, CARD_CANDIDATOS);
      }
      comunicacao=2;
    }

    if (comunicacao==2){ //proc2 espera proc1
      // O proc1 tem entao que esperar
      while (comunicacao==2){
        // SLEEP NO PROC1
        sleep(1000000);
      }
    }
    if (comunicacao==0)
      exit(0);
  }while(1);
  exit(0);
  return 0;
}

int main() {

  volatile int *proc_num;
  proc_num = (int *) ADDR_COUNTER;
  if(*proc_num == 0) {

    proc1();
  }
  else {
    __asm__ ("add $8, $zero, 0x2000\n"
        "sll $8, $8, 8\n"
        "sub $sp, $sp, $8");
    proc2();
  }  
}
